import type { IServiceApi } from "@/lib/Service";
interface IUploadOptions {
    file: File;
    blockSize: number;
    service: IServiceApi;
    onSuccess: Function;
    onFailed: Function;
    onProgress: Function;
}

interface IUploadInfo {
    index: number;
    size: number;
    success: boolean;
    retryCount: number;
}

export const shouldSplitToUpload = (file: File) => {
    return file.size > 1024 * 1024 * 10;
}

export class UploadSplit {
    private option: IUploadOptions;
    private currentIndex: number;
    private maxIndex: number;
    private status: Record<number, IUploadInfo> = {};
    private maxRetryCount: number = 5;
    private uploadFiledId!: string;
    private showAutoUploadNextChunk: boolean = false;

    constructor(config: IUploadOptions) {
        this.option = config;
        this.currentIndex = 0;
        this.maxIndex = Math.ceil(config.file.size / config.blockSize);
    }
    start() {
        this.currentIndex = 0;
        this.showAutoUploadNextChunk = true;
        this.upload(this.currentIndex);
    }
    abort() {
        this.showAutoUploadNextChunk = false;
    }
    private upload(index: number) {
        const file = this.option.file;
        const fileSize = this.option.file.size;
        const blockSize = this.option.blockSize;
        const chunkSize = index * blockSize + blockSize> fileSize ? fileSize - index * blockSize : blockSize;
        const startIndex = index * blockSize;
        const chunkData = file.slice(startIndex, startIndex + chunkSize);
        const formData = new FormData();
        formData.append("file", chunkData);
        this.option.service.send({
            endPoint: "resource.upload",
            header: {
                "Content-Type": "multipart/form-data",
                "Content-Range": `bytes ${startIndex}-${startIndex + chunkSize - 1}/${fileSize}`,
                "Content-Length": chunkSize,
                "Content-Disposition": `attachment; filename=\"${encodeURIComponent(file.name)}\"`,
                "X-File-Mode": "Multipart",
                "X-Chunk-Index": index,
                "X-UPLOAD_FILE_ID": this.uploadFiledId
            },
            data: formData,
            isBinary: true,
        }).then((resp: any) => {
            const fileId = resp.data;
            const status = this.status[index];
            const nextChunkInfo = this.findNextChunkIndex(index);
            const nextIndex = nextChunkInfo.nextIndex;
            this.uploadFiledId = fileId;
            this.status[index] = {
                index: index,
                size: chunkSize,
                success: true,
                retryCount: 1
            };
            this.option.onProgress({
                index: index,
                size: chunkSize,
                success: true,
                retryCount: 0,
                percent: nextChunkInfo.percent
            });
            if(nextIndex >= 0) {
                this.showAutoUploadNextChunk && this.upload(nextIndex);
            } else {
                this.showAutoUploadNextChunk && this.finalMerge();
            }
        })
            .catch((err) => {
                const status = this.status[index];
                if(!this.showAutoUploadNextChunk) {
                    return;
                }
                if(status) {
                    if(status.retryCount+1 > this.maxRetryCount ) {
                        this.option.onFailed(err);
                    } else {
                        status.retryCount++;
                        this.upload(index);
                    }
                } else {
                    this.status[index] = {
                        index: index,
                        size: chunkSize,
                        success: false,
                        retryCount: 1
                    };
                    this.upload(index);
                }
            });
    }
    private finalMerge() {
        this.option.service.send({
            endPoint: "resource.uploadMerge",
            header: {
                "X-File-Mode": "Merge",
                "X-UPLOAD_FILE_ID": this.uploadFiledId
            },
            data: {
                fileName: this.option.file.name,
                fileType: this.option.file.type,
                maxChunkIndex: this.maxIndex
            }
        }).then((resp) => {
            this.option.onSuccess(resp);
        }).catch((err) => {
            this.option.onFailed(err);
        });
    }
    private findNextChunkIndex(index: number) {
        let findNextStatus = false;
        let nextIndex = -1;
        let totalCount = 0;
        for(let i = 0; i < this.maxIndex; i++) {
            if(i === index) {
                totalCount += 1;
            } else {
                const status = this.status[i];
                if(status && status.success) {
                    totalCount += 1;
                } else {
                    if(!findNextStatus) {
                        nextIndex = i;
                        findNextStatus = true;
                    }
                }
            }
        }
        return {
            nextIndex,
            percent: (totalCount / this.maxIndex * 100).toFixed(2)
        };
    }
}
